//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-10-04     ZhangYihua   first version
//
// Description  : 
//################################################################################

module add_pair #(
parameter           OPRD_BW                 = 8,    // bit width of single operand
parameter           OPRD_NUM                = 16,   // number of operands, must 1<=OPRD_NUM
parameter           OPRD_SIGNED             = 1'b0,
parameter           OPRD_REG                = 1'b0,

// the following parameters are calculated automatically
parameter           PAIR_NUM                = OPRD_NUM/2,
parameter           ODD_NUM                 = OPRD_NUM%2,
parameter           SUM_NUM                 = PAIR_NUM+ODD_NUM,
parameter           SUM_BW                  = OPRD_BW+((OPRD_NUM<=1) ? 0 : 1)
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,

input                                       vld_in,
input               [OPRD_NUM*OPRD_BW-1:0]  oprds,
output                                      vld_out,
output              [SUM_NUM*SUM_BW-1:0]    sums
);

//################################################################################
// define local varialbe and localparam
//################################################################################

reg                 [SUM_NUM*SUM_BW-1:0]    add_c;

//################################################################################
// main
//################################################################################

generate if (OPRD_SIGNED==1'b1) begin:G_SIGNED
    always@(*) begin:ADD
        integer         i;
    
        for (i=0; i<PAIR_NUM; i=i+1) begin
             add_c[i*SUM_BW+:SUM_BW] = $signed(oprds[(i*2+0)*OPRD_BW+:OPRD_BW]) + $signed(oprds[(i*2+1)*OPRD_BW+:OPRD_BW]);
        end
    
        for (i=PAIR_NUM; i<SUM_NUM; i=i+1) begin
             add_c[i*SUM_BW+:SUM_BW] = $signed(oprds[(i*2+0)*OPRD_BW+:OPRD_BW]);
        end
    end
end else begin:G_UNSIGNED
    always@(*) begin:ADD
        integer         i;
    
        for (i=0; i<PAIR_NUM; i=i+1) begin
             add_c[i*SUM_BW+:SUM_BW] = $unsigned(oprds[(i*2+0)*OPRD_BW+:OPRD_BW]) + $unsigned(oprds[(i*2+1)*OPRD_BW+:OPRD_BW]);
        end
    
        for (i=PAIR_NUM; i<SUM_NUM; i=i+1) begin
             add_c[i*SUM_BW+:SUM_BW] = $unsigned(oprds[(i*2+0)*OPRD_BW+:OPRD_BW]);
        end
    end
end endgenerate

generate if (OPRD_REG==1'b0) begin:G_NREG
    wire                                        clk_nc;
    wire                                        cke_nc;
    wire                                        rst_n_nc;
    
    assign clk_nc   = clk;
    assign cke_nc   = cke;
    assign rst_n_nc = rst_n;

    assign vld_out  = vld_in;
    assign sums     = add_c;
end else begin:G_REG
    reg                 [SUM_NUM*SUM_BW-1:0]    add_reg;
    reg                                         vld_reg;

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            vld_reg <=`U_DLY 1'b0;
        end else if (cke==1'b1) begin
            vld_reg <=`U_DLY vld_in;
        end else
            ;
    end

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            add_reg <=`U_DLY {SUM_NUM*SUM_BW{1'b0}};
        end else if ((cke==1'b1) && (vld_in==1'b1)) begin
            add_reg <=`U_DLY add_c;
        end else
            ;
    end

    assign vld_out = vld_reg;
    assign sums    = add_reg;
end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
